/*****************************************************************************
 * @brief   nand hal fmc
 *****************************************************************************/
#include "bsp.h"
#include "bsp_xmc_nand.h"
#include "nand_lut.h"
#include "mem_alloc.h"

/*****************************************************************************
 * @brief   INFO
 *****************************************************************************/
uu8 *nand_cache;

uint8_t fmc_nand_ecc_error_get(void);
uint8_t fmc_nand_read_page(uint32_t page_addr);
uint8_t fmc_nand_read_cache(uint16_t cell, uint8_t *dat, uint16_t Len);
uint8_t fmc_nand_read_spare(uint32_t page_addr, uint8_t *dat, uint16_t Len);
uint8_t fmc_nand_write_cache(uint16_t cell, const uint8_t *dat, uint16_t Len);
uint8_t fmc_nand_program_exe(uint32_t page_addr);
uint8_t fmc_nand_block_erase(uint32_t block_addr);
uint8_t nand_power_wait(void);
/*****************************************************************************
 * @brief   hal interface link
 *****************************************************************************/
char nand_fmc_dev_link(nand_lut_type *lut)
{
	dword_tt id;
	char cnt;

	if (nand_cache == 0)
	{
		nand_cache = malloc(4096 + 16);
	}

	// spi nand hal link
	lut->hal.ecc_error_get = fmc_nand_ecc_error_get;
	lut->hal.read_page = fmc_nand_read_page;
	lut->hal.read_cache = fmc_nand_read_cache;
	lut->hal.read_spare = fmc_nand_read_spare;
	lut->hal.write_cache = fmc_nand_write_cache;
	lut->hal.program_exe = fmc_nand_program_exe;
	lut->hal.init = 0;
	lut->hal.block_erase = fmc_nand_block_erase;
	lut->hal.power_wait = nand_power_wait;

	// chip infomation recognize
	for (cnt = 0; cnt < 5; cnt++)
	{
		nand_id_type nid;
		// read ID
		id.uVal = nand_read_id(&nid);

		// w25n 1Gbits
		if (id.uVal == MT29F1G08ABADA)
		{
			lut->page_size = 2048 + 16;
			lut->page_main_size = 2048;
			lut->page_spare_size = 16;
			lut->page_per_block = 64;
			lut->block_per_plane = 1024;
			lut->block_total = 1024;
			lut->logic_block_amount = lut->block_total * 7 / 8;
			lut->keep_err_logic = 0;

			break;
		}
		else
		{
			lut->page_size = 2048 + 16;
			lut->page_main_size = 2048;
			lut->page_spare_size = 16;
			lut->page_per_block = 64;
			lut->block_per_plane = 1024;
			lut->block_total = 1024;
			lut->logic_block_amount = lut->block_total * 7 / 8;
			lut->keep_err_logic = 0;
		}
	}

	return 0;
}

/*****************************************************************************
 * @brief   get ecc
 *****************************************************************************/
uint8_t fmc_nand_ecc_error_get(void) // 1-ecc error, 0-ecc not error
{
	return atio_nand.ecc_err_st;
}

/*****************************************************************************
 * @brief   read page
 *****************************************************************************/
uint8_t fmc_nand_read_page(uint32_t page_addr)
{
	int cnt = 3;

	while (cnt--)
	{
		nand_read_page(nand_cache, page_addr, 0, 0);
		if (0 == atio_nand.ecc_err_st)
		{
			break;
		}
	}

	if (cnt < 0)
	{
		lut_print("nand ecc fail @ page: %d\n", page_addr);
	}

	return 0;
}

/*****************************************************************************
 * @brief   read cache
 *****************************************************************************/
uint8_t fmc_nand_read_cache(uint16_t cell, uint8_t *dat, uint16_t Len)
{
	copy_u8(&nand_cache[cell], dat, Len);

	return 0;
}

/*****************************************************************************
 * @brief   write cache
 *****************************************************************************/
uint8_t fmc_nand_write_cache(uint16_t cell, const uint8_t *dat, uint16_t Len)
{
	copy_u8(dat, &nand_cache[cell], Len);

	return 0;
}

/*****************************************************************************
 * @brief   read spare
 *****************************************************************************/
uint8_t fmc_nand_read_spare(uint32_t page_addr, uint8_t *dat, uint16_t Len)
{
	int cnt = 3;

	while (cnt--)
	{
		nand_read_spare_area(nand_cache, page_addr, 0, 16);
		if (comp_u8(&nand_cache[0], &nand_cache[8], 8) == 0)
			break;
	}

	copy_u8(nand_cache, dat, Len);

	if (cnt < 0)
	{
		atio_nand.ecc_err_st++;
		lut_print("nand spare not compare @ page: %d\n", page_addr);
	}

	return 0;
}

/*****************************************************************************
 * @brief   program
 *****************************************************************************/
uint8_t fmc_nand_program_exe(uint32_t page_addr)
{
	copy_u32(&nand_cache[2048], &nand_cache[2048 + 8], 2);
	nand_write_page(nand_cache, page_addr, 0, 0);

	return 0;
}

/*****************************************************************************
 * @brief   erase
 *****************************************************************************/
uint8_t fmc_nand_block_erase(uint32_t block_addr)
{
	nand_erase_block(block_addr);

	return 0;
}
